<?php
//汇付宝：https://dev.heepay.com/docs/#/KJJK?id=%e5%bf%ab%e6%8d%b7%ef%bc%88%e9%93%b6%e8%a1%8c%e4%be%a7%ef%bc%89
// https://open.heepay.com/www/index.html#/openDoc?type=menu&id=2022210140141144
//这是demo:https://open.heepay.com/www/index.html#/openDoc?type=menu&id=2022800804
namespace jy_heepay;
defined('BY_JYA') or exit('error');
class pay extends \plugin{
    public function __construct($setting=array()){
        parent::__construct('jy_heepay');
        $setting=uni_setting('pay');
        if(!$setting['heepay_merchid'] || !$setting['heepay_private'] || !$setting['heepay_public']){
            exi('商户编号，商户私钥，商户公钥，都必须填写','error');
        }
        if($_SESSION['auth_wxid']){
            $auth_appid=pdo_getcolumn('core_account_wechat',array('id'=>$_SESSION['auth_wxid']),'appid');
        }
        $wechat_appid=$auth_appid?:$setting['wechat_appid'];
        if(!$wechat_appid){
            $wechat_appid=pdo_getcolumn('core_account_wechat',array('uniacid'=>$_SESSION['uniacid'],'isdefault'=>1),'appid');
            if(!$wechat_appid){
                $wechat_appid=pdo_getcolumn('core_account_wechat',array('uniacid'=>$_SESSION['uniacid']),'appid');
            }
        }
		if(strpos($setting['heepay_private'],'BEGIN PRIVATE KEY')===false){
			$setting['heepay_private']="-----BEGIN PRIVATE KEY-----\n".$setting['heepay_private']."\n-----END PRIVATE KEY-----";
		}
		if(strpos($setting['heepay_public'],'BEGIN PUBLIC KEY')===false){
			$setting['heepay_public']="-----BEGIN PUBLIC KEY-----\n".$setting['heepay_public']."\n-----END PUBLIC KEY-----";
		}
        $this->config=array(
            'sub_appid'=>$wechat_appid,
            'heepay_merchid'=>$setting['heepay_merchid'],
            'heepay_private'=>$setting['heepay_private'],
			'heepay_public'=>$setting['heepay_public'],
        );
    }
	//网关签约接口
    public function banksign($pay_log,$paytype='',$wxappid=''){
		$banksign=$this->getbanksign($pay_log);
		if($banksign['sign_url']){
			return $banksign['sign_url'];
		}
		$biz_content = array(
				"out_trade_no" => $banksign['sn'],
				"out_trade_time" => date('Y-m-d H:i:s', TIMESTAMP),
				"merch_user_id" => $pay_log['mid']?:$_SESSION['uid'],
				"notify_url" => $pay_log['notify_url']?custom_url($pay_log['notify_url'],true):SITEROOT .'addons/jy_heepay/notify.php',
				"return_url" => custom_url($pay_log['success_url'],true),
		);
		$biz_content_json = json_encode($biz_content,JSON_UNESCAPED_UNICODE);
        $payment_params = array(
            'method'=>'heepay.agreement.bank.sign.page',
            'version'=>'1.0',
			'merch_id'=>$this->config['heepay_merchid'],
			'timestamp' => date('Y-m-d H:i:s', TIMESTAMP),
			'biz_content' => $biz_content_json,
        );
        $res=$this->request('https://Pay.Heepay.com/API/PageSign/Index.aspx',$payment_params);
        //file_put_contents(ROOT_D.'/log.txt', date('Y-m-d H:i:s', time()) . " 网关签约传参:" . json_encode($payment_params)."\r\n", FILE_APPEND);
        //file_put_contents(ROOT_D.'/log.txt', date('Y-m-d H:i:s', time()) . " 网关签约返回信息:" . ijson_encode($res)."\r\n", FILE_APPEND);
		if($res['code']!='10000'){
			exi('网关签约失败，失败原因:'.$res['code'].$res['msg'].$res['sub_code'].$res['sub_msg'],'error');
		}
		$res=$this->getdata($res['data']);
		$res=json_decode($res,true);
		//file_put_contents(ROOT_D.'/log.txt', date('Y-m-d H:i:s', time()) . " 网关签约返回解密数据:" . ijson_encode($res)."\r\n", FILE_APPEND);
		//$update=array('hy_auth_uid'=>$res['sign_no']);
        //$this->upbanksign($banksign['id'],$update);
		if($res['sign_url']){
			pdo_update('jy_heepay_banksign',array("sign_url"=>$res['sign_url']),array('uniacid'=>$_SESSION['uniacid'],"id"=>$banksign['id']));
		}
        return $res['sign_url'];
    }
    //短信支付
    public function create($pay_log,$isnotify=false){
        $banksign=$this->getbanksign($pay_log,true);
		if(!$banksign['hy_auth_uid']){
			$sign_url=$this->banksign($pay_log);
			return $sign_url;
		}
		$biz_content = array(
				"version" => 1,
				"agent_bill_id" =>$pay_log['tid']?:$pay_log['ordersn'],
				"pay_amt" => $pay_log['fee'],
				"goods_name" => $pay_log['title'],
				"hy_auth_uid" => $banksign['hy_auth_uid'],
				"user_ip" => getip(),
				"agent_bill_time" => date('YmdHis', TIMESTAMP),
				"notify_url" => $pay_log['notify_url']?custom_url($pay_log['notify_url'],true):SITEROOT .'addons/jy_heepay/notify.php',
				"return_url" => '',
		);
		$biz_content_json = $this->pingstr($biz_content);
        $payment_params = array(
			'agent_id'=>$this->config['heepay_merchid'],
			'encrypt_data' => $biz_content_json,
        );
        //file_put_contents(ROOT_D.'/log.txt', date('Y-m-d H:i:s', time()) . " 支付记录:" .$biz_content_json."\r\n", FILE_APPEND);
        $res=$this->request2('https://Pay.Heepay.com/WithholdAuthPay/SendPaySMS.aspx',$payment_params);
        //file_put_contents(ROOT_D.'/log.txt', date('Y-m-d H:i:s', time()) . " 返回信息:" . ijson_encode($res)."\r\n", FILE_APPEND);
		if($res['ret_code']!='0000'){
			exi('支付失败，失败原因:'.$res['ret_msg'],'error');
		}
        $res=$this->getdata($res['encrypt_data']);
        //file_put_contents(ROOT_D.'/log.txt', date('Y-m-d H:i:s', time()) . " 解码:" . $res."\r\n", FILE_APPEND);
		exi('','','jy_heepay/index//sn='.$banksign['sn'].'&'.$res);
    }
	//短信确认支付
    public function checkpay($smslog,$verify_code=''){
		$biz_content = array(
				"version" => 1,
				"hy_token_id" =>$smslog['hy_token_id'],
				"verify_code" => $verify_code,
		);
		$biz_content_json = $this->pingstr($biz_content);
        $payment_params = array(
			'agent_id'=>$this->config['heepay_merchid'],
			'encrypt_data' => $biz_content_json,
        );
        //file_put_contents(ROOT_D.'/log.txt', date('Y-m-d H:i:s', time()) . " 短信确认支付记录:" . $biz_content_json."\r\n", FILE_APPEND);
        $res=$this->request2('https://Pay.Heepay.com/WithholdAuthPay/ConfirmPay.aspx',$payment_params);
        //file_put_contents(ROOT_D.'/log.txt', date('Y-m-d H:i:s', time()) . " 返回信息:" . ijson_encode($res)."\r\n", FILE_APPEND);
		if($res['ret_code']!='0000'){
			exi('短信确认支付失败，失败原因:'.$res['ret_msg'],'error');
		}
        $res=$this->getdata($res['encrypt_data']);
        return $res;
    }
	public function request($url,$post=null,$continue=true,$direct=false,$return_error=false,$isjson=false,$isrequest=false){
		if($post['encrypt_data']){
		    $sign=$this->getSign2($post['encrypt_data']);
	        $post['sign']=$sign;
			$post['encrypt_data']=$this->encrypt_data($post['encrypt_data']);
		}else{
		    $sign=$this->getSign($post);
	        $post['sign']=$sign; 
		}
		if($post['biz_content']){
			$post['biz_content']=$this->encrypt_data($post['biz_content']);
		}
		$post=ijson_encode($post);
		//file_put_contents(ROOT_D.'/log.txt', date('Y-m-d H:i:s', time()) . $url." request记录:" . $post."\r\n", FILE_APPEND);
		$rurl=$url;
		if($isjson){
			$headers = array('Content-Type' => 'application/json');
			$result=cfc('http')->ihttp_json($rurl,$post);
		}elseif($isrequest){
			$result=cfc('http')->ihttp_request($rurl,$post);
		}elseif($post){
			$result=cfc('http')->ihttp_post($rurl,$post);
		}else{
			$result=cfc('http')->ihttp_get($rurl);
		}
		if(is_error($result)){			
			$message="支付错误, 错误: {$result['message']}";
			if($return_error){
				return error($message);
			}else{
				exi($message,'error');
			}
		}
		$data=preg_replace('/[\x00-\x1F]/','',$result['content']);
		$data=@json_decode($data,true);
		if($data['errcode']){
			$message=$this->error_code($data['errcode']).' '.$data['errmsg'].' '.$data['errcode'];
			if($return_error || $_SESSION['this']=='custom'){
				//return error($message);
			}else{
				//exi($message,'error');
			}
		}
		if($direct){
			return $result;
		}
		if(!is_array($data) || !$data){
		    $data=xml2array($result['content']);
		    if(!is_array($data) || !$data){
    			$message='支付错误，以下为原始数据：'.$result['content'];
    			if($return_error){
    				return error($message);
    			}else{
    				exi($message,'error');
    			}
		    }
		}
		return $data;
	}
	public function request2($url,$post=null) {
		$sign=$this->getSign2($post['encrypt_data']);
		$encrypt_data=$this->encrypt_data($post['encrypt_data']);
		// 要发送的数据
		$data = array(
			'agent_id' => $post['agent_id'],
			'sign' => $sign,
			'encrypt_data' => $encrypt_data,
		);
		$options = array(
			'http' => array(
				'header' => "Content-type: application/x-www-form-urlencoded\r\n",
				'method' => 'POST',
				'content' => http_build_query($data),
			),
		);
		// 创建上下文流
		$context = stream_context_create($options);
		// 发送POST请求并获取响应
		$xmlData = file_get_contents($url, false, $context);
		$data=xml2array($xmlData);
		if(!is_array($data) || !$data){
    		$message='支付错误，以下为原始数据：'.$result['content'];
    		if($return_error){
    			return error($message);
    		}else{
    			exi($message,'error');
    		}
		}
		return $data;
	}
    /**
     * 作用：生成签名
     */
    public function getSign($params=array()) {
		$merch_id=$this->config['heepay_merchid'];
		$private_key_str=$this->config['heepay_private'];
		ksort($params); // 按键名对参数数组进行排序
        $stringToBeSigned = "";
        $i = 0;
        foreach ($params as $k => $v) {
            if (false === empty($v) && "@" != substr($v, 0, 1)) {
                if ($i == 0) {
                    $stringToBeSigned .= "$k" . "=" . "$v";
                } else {
                    $stringToBeSigned .= "&" . "$k" . "=" . "$v";
                }
                $i++;
            }
        }
        $signatureContent = str_replace('\/', '/', $stringToBeSigned);
		//file_put_contents(ROOT_D.'/log.txt', date('Y-m-d H:i:s', time()) . " 待签名串:" . $signatureContent."\r\n", FILE_APPEND);
		// 对签名字符串进行的RSA签名
		$private_key = openssl_pkey_get_private($private_key_str);
		//file_put_contents(ROOT_D.'/log.txt', date('Y-m-d H:i:s', time()) . " private_key:" . $private_key_str."\r\n", FILE_APPEND);
		openssl_sign($signatureContent, $signature, $private_key, OPENSSL_ALGO_SHA1);
		$sign = base64_encode($signature);
		//file_put_contents(ROOT_D.'/log.txt', date('Y-m-d H:i:s', time()) . " signature:" . $sign."\r\n", FILE_APPEND);
		return $sign;
    }
	public function getSign2($signatureContent='') {
		$merch_id=$this->config['heepay_merchid'];
		$private_key_str=$this->config['heepay_private'];
		// 对签名字符串进行的RSA签名
		$private_key = openssl_pkey_get_private($private_key_str);
		//file_put_contents(ROOT_D.'/log.txt', date('Y-m-d H:i:s', time()) . " private_key:" . $private_key_str."\r\n", FILE_APPEND);
		openssl_sign($signatureContent, $signature, $private_key, OPENSSL_ALGO_SHA1);
		$sign = base64_encode($signature);
		//file_put_contents(ROOT_D.'/log.txt', date('Y-m-d H:i:s', time()) . " signature:" . $sign."\r\n", FILE_APPEND);
		return $sign;
    }
     public function pingstr($params=array()) {
		ksort($params); // 按键名对参数数组进行排序
        $stringToBeSigned = "";
        $i = 0;
        foreach ($params as $k => $v) {
            if (false === empty($v) && "@" != substr($v, 0, 1)) {
                if ($i == 0) {
                    $stringToBeSigned .= "$k" . "=" . "$v";
                } else {
                    $stringToBeSigned .= "&" . "$k" . "=" . "$v";
                }
                $i++;
            }
        }
        $signatureContent = str_replace('\/', '/', $stringToBeSigned);
		return $signatureContent;
    }
	public function encrypt_data($biz_content_json='') {
		//分段加密方法
		$publicKey = $this->config['heepay_public'];
		// 要加密的数据
		$data = $biz_content_json;
		// 定义每个分段的大小
		$chunkSize = 117; // 2048-bit key can encrypt 117 bytes
		// 分段加密
		$encryptedData = '';
		for ($i = 0; $i < strlen($data); $i += $chunkSize) {
			$chunk = substr($data, $i, $chunkSize);
			openssl_public_encrypt($chunk, $encryptedChunk, $publicKey);
			$encryptedData .= $encryptedChunk;
		}
		// 将分段加密后的数据进行处理（例如Base64编码）并发送给接收方
		$encrypt_data = base64_encode($encryptedData);
		return $encrypt_data;
	}
	public function getdata($data1='') {
		$private_key_str=$this->config['heepay_private'];
		$encryptedData = base64_decode($data1);
		// 定义每个分段的大小
		$chunkSize = 256; // 2048-bit key can decrypt 256 bytes
		$PRIVATEkey=$private_key_str;
		// 分段解密
		$decryptedData = '';
		for ($i = 0; $i < strlen($encryptedData); $i += $chunkSize) {
			$chunk = substr($encryptedData, $i, $chunkSize);
			openssl_private_decrypt($chunk, $decryptedChunk, $PRIVATEkey);
			$decryptedData .= $decryptedChunk;
		}
		return $decryptedData;
	}
	public function getbanksign($pay_log=array(),$isget=false){
		$pay_log['mid']=$pay_log['mid']?:$_SESSION['uid'];
		$banksign=pdo_get('jy_heepay_banksign',array('hy_auth_uid <>'=>'','uniacid'=>$_SESSION['uniacid'],"mid"=>$pay_log['mid']),'','out_trade_time desc');
		if($banksign['id'] && $banksign['hy_auth_uid']){
			return $banksign;
		}
		$banksign=pdo_get('jy_heepay_banksign',array('uniacid'=>$_SESSION['uniacid'],"mid"=>$pay_log['mid']),'','out_trade_time desc');
		if($banksign['id']){
			if(!$isget && !$banksign['hy_auth_uid']){
			    $sn=$pay_log['tid']?:$pay_log['ordersn'];
				$banksign['sn']=$sn?:get_ordersn('jy_heepay_banksign','sn');
				pdo_update('jy_heepay_banksign',array("out_trade_time" => TIMESTAMP,"sn"=>$banksign['sn']),array('uniacid'=>$_SESSION['uniacid'],"id"=>$banksign['id']));
			}
			return $banksign;
		}
		$sn=$pay_log['tid']?:$pay_log['ordersn'];
		$banksign=array(
			'uniacid'=>$_SESSION['uniacid'],
			"sn" => $sn?:get_ordersn('jy_heepay_banksign','sn'),
			"out_trade_time" => TIMESTAMP,
			"mid" => $pay_log['mid'],
			"bank_card_no" => $pay_log['bank_card_no'],
			"bank_user_name" => $pay_log['bank_user_name'],
			"mobile" => $pay_log['mobile'],
			"cert_no" => $pay_log['cert_no'],
			"createtime" => TIMESTAMP,
		);
		pdo_insert('jy_heepay_banksign',$banksign);
		$banksign['id']=pdo_insertid();
		return $banksign;
	}
	public function upsms($pay_log=array(),$hy_token_id=''){
		$pay_log['mid']=$pay_log['mid']?:$_SESSION['uid'];
		$sn=$pay_log['tid']?:$pay_log['ordersn'];
		$sms=pdo_get('jy_heepay_smslog',array('sn'=>$sn,'uniacid'=>$_SESSION['uniacid'],"mid"=>$pay_log['mid']));
		if($sms['id']){
			pdo_update('jy_heepay_smslog',array('hy_token_id'=>$hy_token_id),array('sn'=>$pay_log['tid'],'uniacid'=>$_SESSION['uniacid'],"mid"=>$pay_log['mid']));
			return $banksign;
		}
		$sms=array(
			'uniacid'=>$_SESSION['uniacid'],
			"mid"=>$pay_log['mid'],
			"sn" => $pay_log['tid'],
			'hy_token_id'=>$hy_token_id,
			"createtime" => TIMESTAMP,
		);
		pdo_insert('jy_heepay_smslog',$sms);
		$sms['id']=pdo_insertid();
		return $sms;
	}
}